package com.xy.smartsms.plugincmcc;

import android.app.Activity;
import android.content.Context;
import android.text.TextUtils;
import android.util.LruCache;

import com.xy.smartsms.data.BaseSmsPlugin;
import com.xy.smartsms.data.ISmsPlugin;
import com.xy.smartsms.data.PublicInfoData;
import com.xy.smartsms.data.SmsItem;
import com.xy.smartsms.util.Log;

import org.json.JSONArray;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import cn.cmcc.online.smsapi.SmsCardData;
import cn.cmcc.online.smsapi.SmsPlusApi;
import cn.cmcc.online.smsapi.StringKeyValuePair;

public class CMCCSmsPlugin extends BaseSmsPlugin {
    private static final String TAG = CMCCSmsPlugin.class.getSimpleName();
    private static final String PUBLICINFO_NAME = "name";
    private static final String PUBLICINFO_LOGO = "logo";
    private static final String PUBLICINFO_MENU = "menu";
    private static final String SUB_MENU = "Submenu";
    private static final String ACTION_TYPE = "Opentype";
    private static final String ACTION_URL = "Openurl";
    private static final String MENU_NAME = "Menuname";

    private static final String PARTID_TABLE = "H450;B550;F950";
    private static final String PARTID_PROGRESSBAR = "H450;B550;F950";
    private static final String PARTID_PRIMITIVE = "H450;B551;F950";

    private ISmsPluginAction mSmsPluginAction = null;
    private CMCCDataCache mDataCache = null;
    private static final LruCache<String, PublicInfoData> sPublicInfoCache = new LruCache<>(100);
    private static List<String> sVerifyCodeList = Arrays.asList("验证码", "交易码", "确认码", "认证码", "随机码", "校验码", "激活码", "优惠码",
            "注册码");
    private static List<String> sPasswordCodeList = Arrays.asList("密码");
    private static LinkedHashMap<String, String> sTagIconMap = new LinkedHashMap<>();
    private CardCallable mCardCallable = null;
    private int mParseTimeout = 6000;
    private Object mCardSync = new Object();

    public CMCCSmsPlugin(Context context) {
        super(context);

        mCardCallable = new CardCallable(mContext);
        mDataCache = CMCCDataCache.getInstance(mContext);
        initMap();
    }

    private void initMap() {
        sTagIconMap.put("快递;快件", "CMCC_track_deliveries");
        sTagIconMap.put("官网;网站", "CMCC_open_url");
        sTagIconMap.put("客户端;APP", "CMCC_open_app");
        sTagIconMap.put("活动;优惠;折扣;打折;特惠;专属", "CMCC_promotional_activies");
        sTagIconMap.put("查询;查看;明细;详情", "CMCC_business_query");
        sTagIconMap.put("订购;办理;开通", "CMCC_business_process");
        sTagIconMap.put("常见问题;在线客服;服务大厅;售后;帮助", "CMCC_customer_service");
        sTagIconMap.put("复制", "CMCC_copy_verification_code");
        sTagIconMap.put("积分", "CMCC_operator_point_service");
        sTagIconMap.put("话费", "CMCC_phone_balance");
        sTagIconMap.put("流量", "CMCC_netflow_recharge");
        sTagIconMap.put("热线;联系电话;客服电话;联系我们", "CMCC_hotline_service");
        sTagIconMap.put("微信", "CMCC_follow_wechat");
        sTagIconMap.put("微博", "CMCC_follow_weibo");
        sTagIconMap.put("酒店", "CMCC_r_hotel");
        sTagIconMap.put("就诊;名医", "CMCC_r_hospital");
    }

    public void finalize() throws Throwable {

    }

    @Override
    public void init(Object doAction, Map<String, String> extend) {
        super.init(doAction, extend);
        mParams.put("custom_plugin_type", "data");

        mSmsPluginAction = (ISmsPluginAction) doAction;
        if (extend != null) {
            String timeout = extend.get("custom_parse_time_out");
            if (!TextUtils.isEmpty(timeout)) {
                mParseTimeout = Integer.parseInt(timeout);
            }
        }

        SmsPlusApi.init(mContext);
    }

    private String getAction(String tagName) {
        String result = "CMCC";
        if (TextUtils.isEmpty(tagName)) {
            return result;
        }

        Iterator<String> iterator = sTagIconMap.keySet().iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            String[] keyArray = key.split(";");
            for (String k : keyArray) {
                if (tagName.contains(k)) {
                    result = sTagIconMap.get(key);
                    return result;
                }
            }
        }

        return result;
    }

    private JSONArray buildXYMenu(String menuData, String moduleId) {
        if (menuData == null || menuData.length() == 0) {
            return null;
        }

        JSONArray result = new JSONArray();
        try {
            JSONArray srcArray = new JSONArray(menuData);
            for (int i = 0; i < srcArray.length(); i++) {
                JSONObject menuObj = srcArray.getJSONObject(i);
                String tagName = menuObj.optString("Tagname");
                result.put(new JSONObject().put("action_data", new JSONObject()
                        .put(ACTION_URL, menuObj.optString("Optionurl"))
                        .put(ACTION_TYPE, menuObj.optString("Tagtypes"))
                        .put(MENU_NAME, menuObj.optString("Tagname"))
                        .put("moduleId", moduleId).toString())
                        .put("btn_name", tagName)
                        .put("action", getAction(tagName))
                        .put("ManufactureData", "CMCC"));
            }
        } catch (Throwable t) {
            Log.e(TAG, "buildXYMenu", t);
        }

        return result;
    }

    private JSONArray buildTable(Map<String, String> map) {
        JSONArray array = new JSONArray();
        if (map == null) {
            return array;
        }

        try {
            for (String key : map.keySet()) {
                JSONObject rowObj = new JSONObject();
                int start = 0;
                if (key.startsWith("txt_")) {
                    start = 4;
                }

                rowObj.put("t1", key.substring(start));
                rowObj.put("t2", map.get(key));

                array.put(rowObj);
            }
        } catch (Throwable t) {
            Log.e(TAG, "buildTable", t);
        }

        return array;
    }

    private String removeUnit(String value) {
        Pattern pattern = Pattern.compile("\\D*(\\d*\\.*\\d*)\\D*");
        Matcher matcher = pattern.matcher(value);
        if (matcher.find()) {
            return matcher.group(1);
        } else {
            return value;
        }
    }

    private String getCardCode(JSONArray array) {
        try {
            for (int i = 0; i < array.length(); i++) {
                String t1 = array.getJSONObject(i).optString("t1");
                for (String code : sVerifyCodeList) {
                    if (t1.contains(code)) {
                        return "verify";
                    }
                }

                for (String code : sPasswordCodeList) {
                    if (t1.contains(code)) {
                        return "password";
                    }
                }
            }
        } catch (Throwable t) {
            Log.e(TAG, "getCardCode", t);
        }

        return null;
    }

    private JSONObject buildXYMap(SmsCardData cardData) {
        if (cardData == null || !cardData.isHasModel()) {
            return null;
        }

        String partId;
        JSONObject result = new JSONObject();
        try {
            if (cardData.isProgress() && cardData.getItemList() != null && cardData.getItemList().size() > 0) {
                partId = PARTID_PROGRESSBAR;
                Map<String, String> tcMap = cardData.getTcMap();
                JSONArray array = buildTable(tcMap);
                Log.v(TAG, "buildXYMap: pg table =" + array);
                List<List<StringKeyValuePair>> itemList = cardData.getItemList();
                String display = null;
                for (List<StringKeyValuePair> pairList : itemList) {
                    JSONObject rowObj = new JSONObject();
                    for (StringKeyValuePair pair : pairList) {
                        Log.v(TAG, "buildXYMap: pg pair =" + pair.getKey() + "" + pair.getValue());
                        if (pair.getKey().equalsIgnoreCase("txt_业务名称")) {
                            rowObj.put("t1", pair.getValue());
                        } else if (pair.getKey().equalsIgnoreCase("txt_已使用")) {
                            String value = pair.getValue();
                            rowObj.put("r1", removeUnit(value));
                        } else if (pair.getKey().equalsIgnoreCase("txt_剩余")) {
                            String value = pair.getValue();
                            display = "剩余: " + value;
                            rowObj.put("r2", removeUnit(value));
                        }
                    }

                    rowObj.put("t2", display);
                    Log.v(TAG, "buildXYMap: pg=" + rowObj);
                    array.put(rowObj);
                }

                if (array != null && array.length() >= 0) {
                    result.put("duoqu_table_data_carrier", array);
                }
            } else if (cardData.getTcMap() != null && cardData.getTcMap().size() > 0) {
                partId = PARTID_TABLE;
                Map<String, String> tcMap = cardData.getTcMap();
                JSONArray array = buildTable(tcMap);
                if (array != null && array.length() >= 0) {
                    result.put("duoqu_table_data_carrier", array);
                }

                String cardCode = getCardCode(array);
                if (!TextUtils.isEmpty(cardCode)) {
                    result.put("custom_card_code", cardCode);
                }
            } else {
                partId = PARTID_PRIMITIVE;
            }

            JSONArray menuArray = buildXYMenu(cardData.getMenus(), cardData.getModelId());
            if (menuArray != null) {
                result.put("NEW_ADACTION", menuArray.toString());
            }

            result.put("view_title_name", cardData.getTitle());
            result.put("View_fdes", partId);
        } catch (Throwable t) {
            Log.e(TAG, "buildXYMap", t);
        }

        Log.i(TAG, "buildXYMap: " + result);
        return result;
    }

    private JSONObject getCardData(SmsItem item, boolean needCache) {
        if (!isNetworkAllowed()) {
            Log.i(TAG, "getCardData: network problem");
            return null;
        }

        synchronized (mCardSync) {
            // get data from api.
            mCardCallable.setItem(item);
            SmsCardData cardData = mCardCallable.executeWithTimeout(mParseTimeout);
            JSONObject resultObj = buildXYMap(cardData);
            // note: crash if access the json in ui thread meanwhile convert it to string in work thread
            String result = null;
            if (resultObj != null) {
                result = resultObj.toString();
            }

            if (result == null && !mCardCallable.isTimeout()) {
                Log.i(TAG, "getCardData: null" + " id=" + item.getMsgId());
                result = CMCCDataCache.CARRIER_NULL_RESULT;
            }

            mDataCache.updateCacheData(item, result, needCache);
            return resultObj;
        }
    }

    private JSONObject getResultObj(String result) {
        JSONObject resultObj = null;
        try {
            if (!result.equals(CMCCDataCache.CARRIER_NULL_RESULT)) {
                resultObj = new JSONObject(result);
            }
        } catch (Throwable t) {
            Log.e(TAG, "getResultObj", t);
        }

        return resultObj;
    }

    private void parseCardAsync(final int type, final SmsItem item, final ISmsPluginCallback callback) {
        if (item.isScrolling()) {
            cardCallback(callback, ISmsPlugin.STATUS_FAIL, type, null);
            return;
        }

        // in order to update ui on time
        cardCallback(callback, ISmsPlugin.STATUS_FAIL, type, null);
        getCardPool().execute(new Runnable() {
            public void run() {
                // get data from db.
                String cachedResult = mDataCache.queryCacheResultFromDB(item);
                if (!TextUtils.isEmpty(cachedResult)) {
                    cardCallback(callback, ISmsPlugin.STATUS_SUCCESS, type, getResultObj(cachedResult));
                    return;
                }

                JSONObject result = getCardData(item, true);
                cardCallback(callback, ISmsPlugin.STATUS_SUCCESS, type, result);
            }
        });
    }

    @Override
    public void parseCardSync(final int type, final SmsItem item) {
        getCardData(item, false);
    }

    @Override
    public void parseCard(final int type, final SmsItem item, final ISmsPluginCallback callback) {
        if (type == ISmsPlugin.TYPE_RICH) {
            String cachedResult = mDataCache.queryCacheResult(item);
            if (!TextUtils.isEmpty(cachedResult)) {
                cardCallback(callback, ISmsPlugin.STATUS_SUCCESS, type, getResultObj(cachedResult));
                return;
            }

            parseCardAsync(type, item, callback);
        } else {
            cardCallback(callback, ISmsPlugin.STATUS_FAIL, type, null);
        }
    }

    private PublicInfoCallable getPublicInfoCallable() {
        // avoid pending menu request
        return new PublicInfoCallable(mContext);
    }

    private void parsePublicInfoAsync(final String phone, final ISmsPluginCallback callback) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                PublicInfoCallable callable = getPublicInfoCallable();
                callable.setPhone(phone);
                String infoString = callable.executeWithTimeout(mParseTimeout);
                PublicInfoData data = new PublicInfoData();
                data.manufacture = "CMCC";
                data.json = handleCMCCMenuData(infoString);
                if (TextUtils.isEmpty(infoString)) {
                    publicInfoCallback(callback, ISmsPlugin.STATUS_FAIL, data);
                    return;
                }

                List<PublicInfoData.MenuItem> menuItemList = new ArrayList<PublicInfoData.MenuItem>();
                try {
                    JSONObject infoJson = new JSONObject(infoString);
                    data.name = infoJson.optString(PUBLICINFO_NAME);
                    data.logo = infoJson.optString(PUBLICINFO_LOGO);
                    JSONArray array = infoJson.optJSONArray(PUBLICINFO_MENU);
                    if (array == null || array.length() <= 0) {
                        publicInfoCallback(callback, ISmsPlugin.STATUS_SUCCESS, data);
                        return;
                    }

                    for (int menuIndex = 0; menuIndex < array.length(); menuIndex++) {
                        JSONObject menuObj = array.getJSONObject(menuIndex);
                        PublicInfoData.MenuItem menuItem = new PublicInfoData.MenuItem();
                        menuItem.name = menuObj.optString(MENU_NAME);

                        JSONArray subArray = menuObj.optJSONArray(SUB_MENU);
                        if (subArray != null && subArray.length() > 0) {
                            List<PublicInfoData.SubMenuItem> subMenuItemList = new ArrayList<PublicInfoData.SubMenuItem>();
                            for (int subMenuIndex = 0; subMenuIndex < subArray.length(); subMenuIndex++) {
                                PublicInfoData.SubMenuItem subMenuItem = new PublicInfoData.SubMenuItem();
                                JSONObject subMenuObj = subArray.getJSONObject(subMenuIndex);
                                subMenuItem.name = subMenuObj.optString(MENU_NAME);
                                subMenuItem.json = new JSONObject(subMenuObj, new String[]{MENU_NAME, ACTION_TYPE, ACTION_URL})
                                        .put("ManufactureData", "CMCC");

                                subMenuItemList.add(subMenuItem);
                            }

                            menuItem.subMenuList = subMenuItemList;
                        } else {
                            menuItem.json = new JSONObject(menuObj, new String[]{MENU_NAME, ACTION_TYPE, ACTION_URL})
                                    .put("ManufactureData", "CMCC");
                        }

                        menuItemList.add(menuItem);
                    }

                    data.menuList = menuItemList;
                    sPublicInfoCache.put(phone, data);
                    publicInfoCallback(callback, ISmsPlugin.STATUS_SUCCESS, data);
                    return;
                } catch (Throwable t) {
                    Log.e(TAG, "", t);
                }
            }
        }).start();
    }

    @Override
    public void parsePublicInfo(String phone, Map<String, String> extend, ISmsPluginCallback callback) {
        PublicInfoData data = sPublicInfoCache.get(phone);
        if (data != null) {
            Log.v(TAG, "parsePublicInfo: cache hit:" + data);
            publicInfoCallback(callback, ISmsPlugin.STATUS_SUCCESS, data);
            callback = null; // update anyway
        }

        if (!isNetworkAllowed()) {
            Log.i(TAG, "parsePublicInfo: network problem");
            publicInfoCallback(callback, ISmsPlugin.STATUS_SUCCESS, null);
            return;
        }

        parsePublicInfoAsync(phone, callback);
    }

    private String handleCMCCMenuData(String infoString) {
        if (infoString == null) {
            return null;
        }

        try {
            infoString = infoString.replace("\"Menuname\"", "\"name\"").replace("\"Submenu\"", "\"secondmenu\"");
            JSONObject infoJson = new JSONObject(infoString);
            JSONArray array = infoJson.optJSONArray(PUBLICINFO_MENU);
            if (array == null || array.length() <= 0) {
                return null;
            }

            return array.toString();
        } catch (Throwable t) {
            Log.e(TAG, "handleCMCCMenuData", t);
        }

        return null;
    }

    @Override
    public void processAction(Activity activity, String action, Map<String, String> extend) {
        try {
            JSONObject json = new JSONObject(action);
            String phone = extend.get("phoneNum");
            String moduleId = json.optString("moduleId");
            if (moduleId == null || moduleId.length() == 0) {
                SmsPlusApi.getActionProcessor().processPortServerAction(activity,
                        phone,
                        json.optString(MENU_NAME),
                        json.optString(ACTION_TYPE),
                        json.optString(ACTION_URL));
            } else {
                SmsPlusApi.getActionProcessor().processCardMenuAction(activity,
                        phone,
                        json.optString(MENU_NAME),
                        json.optString(ACTION_TYPE),
                        json.optString(ACTION_URL),
                        moduleId);
            }
        } catch (Throwable t) {
            Log.e(TAG, "processAction", t);
        }
    }

    @Override
    public void backgroundParse(String phoneNum, int limit) {
        long maxTime = CMCCBeforeParseUtil.getMaxTime(mContext, phoneNum);
        List<SmsItem> listObj = mSmsPluginAction.getMessageList(phoneNum, 0, maxTime, limit);
        if (listObj == null || listObj.isEmpty()) {
            return;
        }

        CMCCBeforeParseUtil.setStop();
        CMCCBeforeParseUtil.executeBeforeParse(mContext, this, phoneNum, listObj, maxTime);
    }

    @Override
    public void loadCache(String phone) {
        if (!TextUtils.isEmpty(phone)) {
            CMCCDataCache.getInstance().loadCacheResultsFromDB(phone);
        }
    }

    @Override
    public void clearCache(String phone) {
        CMCCDataCache.getInstance().clearLruCache();
    }
}
